﻿using System;
using System.Linq;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;

public class PackMenuTool 
{
    const string kPackTool = "AndroidAuto/";
    [MenuItem(kPackTool+"SEt Gamma")]
    public static void SetNonLinear()
    {
        PlayerSettings.colorSpace = ColorSpace.Gamma;
    }
    /// <summary>
    /// 1.必须先切换 android 平台；2.jdk,ndk必须配置好；3。settings 的identity要设置一下;选择android-26
    /// </summary>
    /// <returns></returns>
    [MenuItem(kPackTool+"1: Export Gradle Project(Mono)", false, 101)]
    public static bool ExportGradleProject()
    {
        //TODO:default oritation 设置一下
        //TODO:现在打包mono 并不支持arm64(unity_2018)
        if (PlayerSettings.applicationIdentifier == "com.Company.ProductName")
        {
            PlayerSettings.applicationIdentifier = "com.stone.lolgame";
            //EditorUtility.DisplayDialog("", "", "Ok");
            Debug.LogWarning("未设置app唯一标识，已自动在PlayerSettings将 app identity 为：com.stone.lolgame");
        }
        //build settings
     //   if (!ValidateConfig()) { return false; }

        //PlayerSettings.applicationIdentifier = "cn.noodle1983.unitypatchdemo";
        //PlayerSettings.companyName = "noodle1983";
        //PlayerSettings.productName = "UnityAndroidIl2cppPatchDemo";
        EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
        //PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP);
        PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.Mono2x);
        PlayerSettings.stripEngineCode = false;
        //代码自动勾选
        EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
        EditorUserBuildSettings.allowDebugging = true;
        EditorUserBuildSettings.development = true;
        
        //EditorUserBuildSettings.mini
#if UNITY_2018


        //PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7 | AndroidArchitecture.X86 | AndroidArchitecture.ARM64;
        PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7 | AndroidArchitecture.X86;
        //2019已经没有支持x86了
#endif

        //export project
        string error_msg = string.Empty;
        //string[] levels = new string[] { "Assets/FogOfWar/Samples/Test3D/FogOfWarTestScene.unity",
        //    "Assets/AndroidIl2cppPatchDemo/Scene/0.unity" };
        var scenes = EditorBuildSettings.scenes;
        List<string> lvls = new List<string>();
        foreach (var scene in scenes)
        {
            lvls.Add(scene.path);
        }
        string[] levels = lvls.ToArray();
        //BuildOptions options = BuildOptions.AcceptExternalModificationsToPlayer;
        BuildOptions options = BuildOptions.AllowDebugging|BuildOptions.ConnectWithProfiler|BuildOptions.Development;
        var ANDROID_EXPORT_PATH = AndroidBuilder.ANDROID_EXPORT_PATH;
        if (Directory.Exists(ANDROID_EXPORT_PATH)) { FileUtil.DeleteFileOrDirectory(ANDROID_EXPORT_PATH); }
        Directory.CreateDirectory(ANDROID_EXPORT_PATH);
        try
        {
#if UNITY_2018 || UNITY_2019
            error_msg = BuildPipeline.BuildPlayer(levels,
                ANDROID_EXPORT_PATH,
                EditorUserBuildSettings.activeBuildTarget,
                options).summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded ? string.Empty : "Failed to export project!";
#else
            error_msg = BuildPipeline.BuildPlayer(levels, ANDROID_EXPORT_PATH, EditorUserBuildSettings.activeBuildTarget, options);
#endif
        }
        catch (Exception e)
        {
            Debug.LogError(e.ToString());
            return false;
        }

        if (!string.IsNullOrEmpty(error_msg))
        {
            Debug.LogError(error_msg);
            return false;
        }
        //var PROJECT_DIR = AndroidBuilder.PROJECT_DIR;
        //var EXPORTED_ASSETS_PATH = AndroidBuilder.EXPORTED_ASSETS_PATH;
        //string copyPatchFile = PROJECT_DIR + "/../Assets/PackAndroidAuto/PrebuiltPatches/AllAndroidPatchFiles_Version1.zip";
        //if (File.Exists(copyPatchFile))
        //{
        //    //copy the prebuild patch to the assets directory instead of downloading.
        //    FileUtil.CopyFileOrDirectory(
        //        PROJECT_DIR + "/Assets/PackAndroidAuto/PrebuiltPatches/AllAndroidPatchFiles_Version1.zip",
        //        EXPORTED_ASSETS_PATH + "/AllAndroidPatchFiles_Version1.zip");
        //    FileUtil.CopyFileOrDirectory(
        //        PROJECT_DIR + "/Assets/PackAndroidAuto/PrebuiltPatches/AllAndroidPatchFiles_Version2.zip",
        //        EXPORTED_ASSETS_PATH + "/AllAndroidPatchFiles_Version2.zip");
        //}
        Debug.Log("Export Gradle Project(Mono) 完成");
        return true;
    }

    [MenuItem(kPackTool+"2(nil): Patch Gradle Project", false, 102)]
    public static bool PatchAndroidProject()
    {
        return true;
    }
    /// <summary>
    /// 不知道为什么Menu1：输出的项目名：unityLibrary，所以要在 PlayerSetting 改ProductName "UnityAndroidIl2cppPatchDemo" to "unityLibrary";
    /// 添加\"避免bat执行路径有空格;
    /// apksigne.bat需要添加JAVA_HOME；
    /// </summary>
    /// <returns></returns>
    [MenuItem(kPackTool+"4: Generate Build Scripts", false, 104)]
    public static bool GenerateBuildScripts()
    {
        //下面代码，需要给根据自身平台，修改一下内容：
        //例如我的项目：用Unity2019.4,使用了官方推荐下载的jdk ,sdk
        //ANDROID_PLATFORM  == android-29
        //
        //string sdkPath = EditorPrefs.GetString("AndroidSdkRoot", "");
        //string jdkPath = EditorPrefs.GetString("JdkPath", ""); 
        string sdkPath = @"D:/Program Files/UnityHubPack/2019.4.30f1c3/Editor/Data/PlaybackEngines/AndroidPlayer\SDK";
        string jdkPath = @"D:/Program Files/UnityHubPack/2019.4.30f1c3/Editor/Data/PlaybackEngines/AndroidPlayer\OpenJDK";
        if (string.IsNullOrEmpty(sdkPath) || string.IsNullOrEmpty(jdkPath))
        {
            Debug.LogError("sdk/jdk path is empty! please config via menu path:Edit/Preference->External tools.");
            return false;
        }

        StringBuilder allCmd = new StringBuilder();

        //var ANDROID_BUILD_TOOLS_VERSION = AndroidBuilder.ANDROID_BUILD_TOOLS_VERSION;//26.0.2
        //var ANDROID_PLATFORM = AndroidBuilder.ANDROID_PLATFORM;//android-23
        var ANDROID_BUILD_TOOLS_VERSION = "30.0.2";//by D:\Program Files\UnityHubPack\2019.4.30f1c3\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\build-tools
        var ANDROID_PLATFORM  = "android-29";//by D:\Program Files\UnityHubPack\2019.4.30f1c3\Editor\Data\PlaybackEngines\AndroidPlayer\SDK
        var R_JAVA_PATH = AndroidBuilder.R_JAVA_PATH;
        var MANIFEST_XML_PATH = AndroidBuilder.MANIFEST_XML_PATH;
        var RES_PATH = AndroidBuilder.RES_PATH;
        // gen R.java
        string buildToolPath = sdkPath + "/build-tools/" + ANDROID_BUILD_TOOLS_VERSION + "/";
        string aaptPath = buildToolPath + "/aapt.exe";//后面调用call 时已加了双引号处理，所以这exe不需要额外加 \"
        string platformJar = "\"" + sdkPath + "/platforms/" + ANDROID_PLATFORM + "/android.jar\"";
        string genRJavaCmd = " package -f -m "
            + " -J " + R_JAVA_PATH
            + " -M " + MANIFEST_XML_PATH
            + " -S " + RES_PATH
            + " -I " + platformJar;
        if (!Directory.Exists(R_JAVA_PATH)) { Directory.CreateDirectory(R_JAVA_PATH); }
        allCmd.AppendFormat("call \"{0}\" {1}\n\n", aaptPath, genRJavaCmd);

        var JAR_LIB_PATH = AndroidBuilder.JAR_LIB_PATH;
        var ANDROID_PROJECT_PATH = AndroidBuilder.ANDROID_PROJECT_PATH;
        var JAVA_OBJ_PATH = AndroidBuilder.JAVA_OBJ_PATH;
        var JAVA_SRC_PATH = AndroidBuilder.JAVA_SRC_PATH;
        //build java
        string javacPath = jdkPath + "/bin/javac.exe";
        if (Directory.Exists(JAR_LIB_PATH) == false)
        {
            Debug.Log("自动添加lib目录："+JAR_LIB_PATH);
            Directory.CreateDirectory(JAR_LIB_PATH);
        }
        string[] jarLibFiles = Directory.GetFiles(JAR_LIB_PATH, "*.jar", SearchOption.AllDirectories);
        string[] javaSrcFiles = Directory.GetFiles(ANDROID_PROJECT_PATH, "*.java", SearchOption.AllDirectories);
        string compileParam = "-d " + JAVA_OBJ_PATH
            + " -source 1.7 -target 1.7 "
            + " -classpath " + string.Join(";", jarLibFiles)
            + " -sourcepath " + JAVA_SRC_PATH
            + " -bootclasspath \""
            + platformJar + " " + string.Join(" ", javaSrcFiles)
            + "\"";
        if (!Directory.Exists(JAVA_OBJ_PATH)) { Directory.CreateDirectory(JAVA_OBJ_PATH); }
        allCmd.AppendFormat("call \"{0}\" {1}\n\n", javacPath, compileParam);

        var BUILD_SCRIPTS_PATH = AndroidBuilder.BUILD_SCRIPTS_PATH;
        //dx
        string dxPath = buildToolPath + "/dx.bat";
        string pkgRawPath = BUILD_SCRIPTS_PATH + "/pkg_raw";
        string dexPath = pkgRawPath + "/classes.dex";
        string deParam = " --dex --output=" + dexPath + " " + JAVA_OBJ_PATH + " " + string.Join(" ", jarLibFiles);
        if (!Directory.Exists(pkgRawPath)) { Directory.CreateDirectory(pkgRawPath); }
        allCmd.AppendFormat("call \"{0}\" {1}\n\n@echo on\n\n", dxPath, deParam);

        var SO_LIB_PATH = AndroidBuilder.SO_LIB_PATH;
        //prepare lib
        string outputLibPath = pkgRawPath + "/lib";
        if (Directory.Exists(outputLibPath)) { FileUtil.DeleteFileOrDirectory(outputLibPath); }
        Directory.CreateDirectory(outputLibPath);
        FileUtil.ReplaceDirectory(SO_LIB_PATH + "/armeabi-v7a", outputLibPath + "/armeabi-v7a");
#if UNITY_2019_1_OR_NEWER
//todo:不知道为什么2019.4， 没有了x86 框架的支持
#else
        FileUtil.ReplaceDirectory(SO_LIB_PATH + "/x86", outputLibPath + "/x86");
#endif
#if UNITY_2019
//todo:不知道为什么2019.4， 没有了arm64-v8a 框架的支持
#elif UNITY_2018
        FileUtil.ReplaceDirectory(SO_LIB_PATH + "/arm64-v8a", outputLibPath + "/arm64-v8a");
        FileUtil.DeleteFileOrDirectory(outputLibPath + "/arm64-v8a/Data");
#endif
        FileUtil.DeleteFileOrDirectory(outputLibPath + "/armeabi-v7a/Data");
        FileUtil.DeleteFileOrDirectory(outputLibPath + "/x86/Data");
        var debug_files = Directory.GetFiles(outputLibPath, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".debug") || s.EndsWith(".map") || s.EndsWith(".sym"));
        foreach (string file in debug_files) { File.Delete(file); }

        List<string> file_added = new List<string>();
        var EXPORTED_ASSETS_PATH = AndroidBuilder.EXPORTED_ASSETS_PATH;
        //build apk
        string binPath = BUILD_SCRIPTS_PATH + "/bin";
        if (!Directory.Exists(binPath)) { Directory.CreateDirectory(binPath); }
        string unaligned_apk_path = binPath + "/" + Application.identifier + "." + ".unaligned.apk";
        string assetsPath = EXPORTED_ASSETS_PATH;
        string buildApkParam = " package -f -m -F " + unaligned_apk_path + " -M " + MANIFEST_XML_PATH + " -A " + assetsPath + " -S " + RES_PATH + " -I " + platformJar
            + " " + pkgRawPath;
        allCmd.AppendFormat("call \"{0}\" {1}\n\n", aaptPath, buildApkParam);

        //align
        string zipalign = buildToolPath + "/zipalign.exe";
        string alignedApkName = Application.identifier + ".apk";
        string alignedApkPath = binPath + "/" + alignedApkName;
        string zipalignParam = " -f 4 " + unaligned_apk_path + " " + alignedApkPath;
        allCmd.AppendFormat("call \"{0}\" {1}\n\n", zipalign, zipalignParam);


        var PROJECT_DIR = AndroidBuilder.PROJECT_DIR;
        //sign
        string keystoreDir = PROJECT_DIR + "/AndroidKeystore";
        if (!Directory.Exists(keystoreDir)) { Directory.CreateDirectory(keystoreDir); }
        string keystoreFile = keystoreDir + "/test.keystore";
        if (!File.Exists(keystoreFile))
        {
            string keytoolPath = jdkPath + "/bin/keytool.exe";
            string genKeyParam = "-genkey -alias test -validity 1000 -keyalg RSA -keystore " + keystoreFile + " -dname \"CN = Test, OU = Test, O = Test, L = Test, S = Test, C = Test\" -keysize 4096 -storepass testtest -keypass testtest";
            if (!AndroidBuilder.Exec(keytoolPath, genKeyParam))
            {
                Debug.LogError("exec failed:" + keytoolPath + " " + genKeyParam);
                return false;
            }
        }
        string apksignerPath = buildToolPath + "/apksigner.bat";
        string signParam = " sign --ks " + keystoreFile + " --ks-pass pass:testtest --key-pass pass:testtest " + alignedApkPath;
        allCmd.AppendFormat("call \"{0}\" {1}\n\n", apksignerPath, signParam);
        file_added.Add(apksignerPath);

        //del tmp apk
        allCmd.AppendFormat("del /f /a /Q {0}\n\n", unaligned_apk_path.Replace("/", "\\"));
        allCmd.AppendFormat("explorer.exe {0} \n\n", binPath.Replace("//", "/").Replace("/", "\\"));
        allCmd.AppendFormat("@echo on\n\n"); //explorer as the last line wont return success, so...
        File.WriteAllText(BUILD_SCRIPTS_PATH + "/build_apk.bat", allCmd.ToString());
        file_added.Add(BUILD_SCRIPTS_PATH + "/build_apk.bat");

        //bugs for android-23
        //https://issuetracker.unity3d.com/issues/android-build-fails-with-failed-to-repackage-resources-error-when-api-level-23-or-lower-is-used
        string manifestXmlPath = MANIFEST_XML_PATH;
        string content = File.ReadAllText(manifestXmlPath);
        File.WriteAllText(manifestXmlPath, content.Replace(@"|density", ""));
        file_added.Add(manifestXmlPath);
        Debug.Log("使用BuildTool:<color=red>" + ANDROID_BUILD_TOOLS_VERSION + "</color> Platform:<color=red>" + ANDROID_PLATFORM+"</color>");
        Debug.Log("已生成文件(未记录全部）：\n"+string.Join("\n", file_added.ToArray()));
        return true;
    }

    [MenuItem(kPackTool+"5: Build Apk File", false, 105)]
    public static bool BuildApk()
    {
        var BUILD_SCRIPTS_PATH = AndroidBuilder.BUILD_SCRIPTS_PATH;
        string buildApkPath = BUILD_SCRIPTS_PATH + "/build_apk.bat";
        string alignedApkName = Application.identifier + ".apk";
        string alignedApkPath = BUILD_SCRIPTS_PATH + "/bin/" + alignedApkName;
        //TODO:若失败，打印buildApk.bat 的失败日志
        if (!AndroidBuilder.Exec(buildApkPath, ""))
        {
            Debug.LogError("exec failed:" + buildApkPath);
            return false;
        }

        if (!File.Exists(alignedApkPath))
        {
            Debug.LogError("apk not found:" + alignedApkPath + ", exec failed:" + buildApkPath);
            return false;
        }

        
        Debug.Log("已生成 Apk(aligned):" + alignedApkPath);
        return true;
    }
}
